#include "Vec3f.h"
#include <math.h>
Vec3f::Vec3f()
	: x_(0), y_(0), z_(0)
{
}

Vec3f::Vec3f(const Vec3f &rhs)
	: x_(rhs.x_), y_(rhs.y_), z_(rhs.z_)
{
}

Vec3f::Vec3f(Vec3f &&rvalue)
	: x_(rvalue.x_), y_(rvalue.y_), z_(rvalue.z_)
{
}

Vec3f::Vec3f(float x, float y, float z)
	: x_(x), y_(y), z_(z)
{
}

Vec3f::~Vec3f()
{
}

float Vec3f::length() const
{
	return sqrtf(x_*x_ + y_*y_ + z_*z_);
}

float Vec3f::distance(const Vec3f &rhs) const
{
	return sqrtf(powf((x_-rhs.x()), 2.0f) + pow((y_-rhs.y()), 2.0f) + pow((z_-rhs.z()), 2.0f));
}

float Vec3f::distance(float x, float y, float z) const
{
	return sqrtf(powf((x_-x), 2.0f) + pow((y_-y), 2.0f) + pow((z_-z), 2.0f));
}

Vec3f Vec3f::cross(const Vec3f &rhs) const
{
	return Vec3f((y_*rhs.z() - z_*rhs.y()),
				 (z_*rhs.x() - x_*rhs.z()),
				 (x_*rhs.y() - y_*rhs.x()));
}

Vec3f Vec3f::cross(float x, float y, float z) const
{
	return Vec3f((y_*z - z_*y),
				 (z_*x - x_*z),
				 (x_*y - y_*x));
}

float Vec3f::dot(const Vec3f &rhs) const
{
	return x_*rhs.x() + y_*rhs.y() + z_*rhs.z();
}

float Vec3f::dot(float x, float y, float z) const
{
	return x_*x + y_*y + z_*z;
}

Vec3f Vec3f::normalize()
{
	auto len = length();
	if(len != 0.0f)
	{
		x_ /= len;
		y_ /= len;
		z_ /= len;
	}
	return *this;
}

Vec3f Vec3f::invert()
{
	x_ *= -1.0f;
	y_ *= -1.0f;
	z_ *= -1.0f;
	return *this;
}

Vec3f Vec3f::inverted() const
{
	float x = x_ * -1.0f;
	float y = y_ * -1.0f;
	float z = z_ * -1.0f;
	return Vec3f(x,y,z);
}

Vec3f Vec3f::reflect(const Vec3f &rhs) const
{
	return rhs - ((*this) * 2.0f * dot(rhs));
}

Vec3f Vec3f::refract(const Vec3f &rhs, float eta) const
{
	Vec3f R;
	float k = 1.0f - eta * eta * (1.0f - dot(rhs) * dot(rhs));
	if (k < 0.0f)
        R = Vec3f();
    else
        R = (rhs * eta)  - (*this) * (eta * dot(rhs) + sqrtf(k));
	return R;
}

Vec3f Vec3f::clamp(float a, float b)
{
	x_ = clampf(x_,a,b);
	y_ = clampf(y_,a,b);
	z_ = clampf(z_,a,b);
	return *this;
}

Vec3f Vec3f::clamp(const Vec3f &vec, float a, float b)
{
	Vec3f C;
	C.x() = clampf(vec.x(), a,b);
	C.y() = clampf(vec.y(), a,b);
	C.z() = clampf(vec.z(), a,b);
	return C;
}

float Vec3f::clampf(float x, float a, float b)
{
	return ((x) < (a) ? (a) : ((x) > (b) ? (b) : (x)));
}

Vec3f Vec3f::max(const Vec3f &rhs) const
{
	return ((*this)<(rhs)?(rhs):(*this));
}

Vec3f Vec3f::min(const Vec3f &rhs) const
{
	return ((*this)>(rhs)?(rhs):(*this));
}

Vec3f Vec3f::screen(const Vec3f &rhs) const
{
	Vec3f temp_lhs = (*this) * 255.0f;
	Vec3f temp_rhs = rhs * 255.0f;
	temp_lhs.x() = ((float)(255 - (int)(255.0f-temp_lhs.x())*(255.0f-temp_rhs.x())/255.0f))/255.0f;
	temp_lhs.y() = ((float)(255 - (int)(255.0f-temp_lhs.y())*(255.0f-temp_rhs.y())/255.0f))/255.0f;
	temp_lhs.z() = ((float)(255 - (int)(255.0f-temp_lhs.z())*(255.0f-temp_rhs.z())/255.0f))/255.0f;
	return temp_lhs;
}

Vec3f Vec3f::colorDodge(const Vec3f &rhs) const
{
	Vec3f temp_lhs = (*this) * 255.0f;
	Vec3f temp_rhs = rhs * 255.0f;

	if((int)temp_lhs.x() != 255) temp_lhs.x() = (255.0f * temp_rhs.x()) / (255.0f - temp_lhs.x());
	if((int)temp_lhs.y() != 255) temp_lhs.y() = (255.0f * temp_rhs.y()) / (255.0f - temp_lhs.y());
	if((int)temp_lhs.z() != 255) temp_lhs.z() = (255.0f * temp_rhs.z()) / (255.0f - temp_lhs.z());

	temp_lhs /= 255.0f;
	temp_lhs.clamp(0.0f, 1.0f);
	return temp_lhs;
}

Vec3f Vec3f::colorBurn(const Vec3f &rhs) const
{
	Vec3f temp_lhs = (*this) * 255.0f;
	Vec3f temp_rhs = rhs * 255.0f;

	if(temp_rhs.x() > 0.0f) temp_lhs.x() = 255.0f - ((255.0f * (255.0f - temp_lhs.x())) / temp_rhs.x()); else temp_lhs.x() = 0.0f;
	if(temp_rhs.y() > 0.0f) temp_lhs.y() = 255.0f - ((255.0f * (255.0f - temp_lhs.y())) / temp_rhs.y()); else temp_lhs.y() = 0.0f;
	if(temp_rhs.z() > 0.0f) temp_lhs.z() = 255.0f - ((255.0f * (255.0f - temp_lhs.z())) / temp_rhs.z()); else temp_lhs.z() = 0.0f;

	temp_lhs /= 255.0f;
	temp_lhs.clamp(0.0f, 1.0f);
	return temp_lhs;
}

Vec3f Vec3f::add(const Vec3f &rhs) const
{
	Vec3f temp = *this + rhs;
	temp.clamp(0.0f, 1.0f);
	return temp;
}

Vec3f Vec3f::burn(const Vec3f &rhs) const
{
	Vec3f temp = *this + rhs - 1.0f;
	temp.clamp(0.0f, 1.0f);
	return temp;
}

Vec3f Vec3f::overlay(const Vec3f &rhs) const
{
	Vec3f temp_lhs = (*this) * 255.0f;
	Vec3f temp_rhs = rhs * 255.0f;

	if(temp_rhs.x() <= 128.0f) temp_lhs.x() = 2.0f * temp_rhs.x() * temp_lhs.x() / 255.0f; else 255.0f - 2.0f * (255.0f - temp_rhs.x()) * (255.0f - temp_lhs.x())/255.0f;
	if(temp_rhs.y() <= 128.0f) temp_lhs.y() = 2.0f * temp_rhs.y() * temp_lhs.y() / 255.0f; else 255.0f - 2.0f * (255.0f - temp_rhs.y()) * (255.0f - temp_lhs.y())/255.0f;
	if(temp_rhs.z() <= 128.0f) temp_lhs.z() = 2.0f * temp_rhs.z() * temp_lhs.z() / 255.0f; else 255.0f - 2.0f * (255.0f - temp_rhs.z()) * (255.0f - temp_lhs.z())/255.0f;
	
	temp_lhs /= 255.0f;
	temp_lhs.clamp(0.0f, 1.0f);
	return temp_lhs;
}